/**
 * 
 */
package org.dragonfire.editor.z80;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;

/**
 * @author Darth Android
 * @date Oct 5, 2007
 */
public class AsmOutlinePane extends ContentOutlinePage implements IDocumentListener, ITreeContentProvider, ILabelProvider {

	public static final String LABEL_TEXT = "Labels";
	public static final String IMPORT_TEXT = "Imports";
	public static final Pattern LABEL_PAT = Pattern.compile("^\\s*([A-z0-9_\\.]+)\\:");
	public static final Pattern IMPORT_PAT = Pattern.compile("^\\s*[\\#\\.]include(?:src)?\\s+(.*)(\\;.*)?",
			Pattern.CASE_INSENSITIVE);

	public static class Label {

		private String text;
		private int line;

		/**
		 * @return the text
		 */
		public String getText() {
			return text;
		}

		/**
		 * @return the line
		 */
		public int getLine() {
			return line;
		}

		/**
		 * @param text
		 * @param line
		 */
		public Label(String text, int line) {
			super();
			this.text = text;
			this.line = line;
		}

		public String toString() {
			return getText() + ":" + getLine();
		}

	}

	public static class Import extends Label {

		/**
		 * @param text
		 * @param line
		 */
		public Import(String text, int line) {
			super(text, line);
		}

		public String toString() {
			return getText();
		}

	}

	private Label[] labels;
	private Import[] imports;
	private AsmEditor editor;

	public AsmOutlinePane(AsmEditor ae) {
		this.editor = ae;
		ae.getDocument().addDocumentListener(this);
	}

	/**
	 * {@inheritDoc}
	 */
	public Object[] getChildren(Object parentElement) {
		if (parentElement == LABEL_TEXT)
			return labels;
		if (parentElement == IMPORT_TEXT)
			return imports;
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	public Object getParent(Object element) {
		for (Label label : labels) {
			if (element == label)
				return LABEL_TEXT;
		}
		for (Import importI : imports) {
			if (element == importI)
				return IMPORT_TEXT;
		}
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean hasChildren(Object element) {
		return element == IMPORT_TEXT || element == LABEL_TEXT;
	}

	/**
	 * {@inheritDoc}
	 */
	public Object[] getElements(Object inputElement) {
		return new String[] {
				IMPORT_TEXT, LABEL_TEXT
		};
	}

	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		update(newInput);
	}

	/**
	 * {@inheritDoc}
	 */
	public Image getImage(Object element) {
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	public String getText(Object element) {
		return element.toString();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
	 */
	public void addListener(ILabelProviderListener listener) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
	 *      java.lang.String)
	 */
	public boolean isLabelProperty(Object element, String property) {
		// TODO Auto-generated method stub
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
	 */
	public void removeListener(ILabelProviderListener listener) {
		// TODO Auto-generated method stub

	}

	public void createControl(Composite parent) {
		super.createControl(parent);
		TreeViewer viewer = getTreeViewer();
		viewer.setContentProvider(this);
		viewer.setLabelProvider(this);
		viewer.addSelectionChangedListener(this);
		viewer.setInput(editor.getDocument());
	}

	/**
	 * {@inheritDoc}
	 */
	public void documentAboutToBeChanged(DocumentEvent event) {
		// TODO Auto-generated method stub

	}

	/**
	 * {@inheritDoc}
	 */
	public void documentChanged(DocumentEvent event) {
		update(getTreeViewer().getInput());
	}

	public void update(Object document) {
		if (document == null) {
			labels = new Label[0];
			imports = new Import[0];
			return;
		}
		ArrayList<Label> myLabels = new ArrayList<Label>();
		ArrayList<Import> myImports = new ArrayList<Import>();
		IDocument input = (IDocument) document;

		int lines = input.getNumberOfLines();
		for (int i = 0; i < lines; i++) {
			try {
				String line = input.get(input.getLineOffset(i), input.getLineLength(i));
				Matcher mL = LABEL_PAT.matcher(line);
				Matcher mI = IMPORT_PAT.matcher(line);
				if (mL.find()) {
					myLabels.add(new Label(mL.group(1), i + 1));
				}
				if (mI.find()) {
					String[] files = mI.group(1).split(",");
					for (String file : files) {
						String fname = file.substring(file.indexOf("\"") + 1, file
								.indexOf("\"", file.indexOf("\"") + 1));
						myImports.add(new Import(fname, i + 1));
					}
				}
			} catch (BadLocationException bl) {
				// ignore
			}

		}
		labels = myLabels.toArray(new Label[myLabels.size()]);
		imports = myImports.toArray(new Import[myImports.size()]);
		getTreeViewer().refresh();
	}

	public void selectionChanged(SelectionChangedEvent se) {
		try {
			ITreeSelection select = (ITreeSelection) getTreeViewer().getSelection();
			if (select.getFirstElement() instanceof Label) {
				Label l = (Label) select.getFirstElement();
				IDocument doc = editor.getDocument();
				int start = doc.getLineOffset(l.getLine() - 1);
				int length = doc.getLineLength(l.getLine() - 1);
				editor.getSource().setSelectedRange(start, length);
				editor.getSource().revealRange(start, length);
			}
		} catch (BadLocationException bl) {
			// ignore
		}
	}
}
